package Walker;

import java.util.ArrayList;
import java.util.Hashtable;

import org.antlr.runtime.ANTLRFileStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;


import CompilerFront.PythonParser;
import CompilerFront.PythonLexer;
import CompilerFront.PythonParser.return_stmt_return;
import CompilerFront.PythonTokenSource;

public class Walker {

	//private Hashtable rootSymbolTable;
	private CommonTree fileRoot;
	//patch 
	public static final int WIHLE_TYPE = 103;
	public static final int IF_TYPE = 91;
	public static final int RELATION_OP = 33;
	public static final int RETURN_TYPE = 101;
	public static final int IMPORT_TYPE = 92;
	public static int newCount = 0;         //identify different newVariable
	private static final String INDENT = "    ";
	
	public Walker(CommonTree root)
	{
		this.fileRoot = root;
		
	}
	
	/**
	 * Highest level of parsing
	 */
	public void traverseRoot()
	{
		int i;
		int childCount = fileRoot.getChildCount();
		Hashtable<String, String> symboltab = new Hashtable<String, String>();
		//System define
		symboltab.put("True", "true");
		symboltab.put("False", "false");
		symboltab.put("sys", "process");
		symboltab.put("and", " && ");
		symboltab.put("not", "!");
		
		for(i = 0; i < childCount; i++)
		{
			//Hashtable<String, String> temp = (Hashtable<String, String>)symboltab.clone();
			switch(fileRoot.getChild(i).getType())
			{
				case  PythonParser.SIMPLE_STATMENT:
				{
					traverseSimpleStmt(fileRoot.getChild(i), symboltab,"");
					break;
				}
				case PythonParser.FUNCTION_DEF:
				{
					traverseFundef(fileRoot.getChild(i), symboltab, "");
					//insert funName into hash table
					// addToSymbolTable(symboltab, funName);
					break;
				}
				case PythonParser.BLOCK:
				{
					traverseBlock(fileRoot.getChild(i), symboltab, "");
					//insert funName into hash table
					// addToSymbolTable(symboltab, funName);
					break;
				}
				default:
				{
					System.out.println("Error at child[" + i + "], root");
					//System.exit(0);
					break;
				}
				
			}
		}
	}
	
	/**
	 * parse fun and return its name
	 * @param root
	 * @param symtab
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected String traverseFundef(Tree root, Hashtable<String, String> symtab, String padding)
	{
		assert(root.getChildCount() == 3);
		Hashtable<String, String> symbolTab; 
		String funName = root.getChild(0).getText();
		addToSymbolTable(symtab, funName);
		symbolTab = (Hashtable<String, String>)symtab.clone();
		/* parse function parameters */
		ArrayList<String> parameters = traverseParameters(root.getChild(1));
		int i;
		String paramStr = "";
		for(i = 0; i < parameters.size(); i++)
		{
			addToSymbolTable(symbolTab, parameters.get(i));
			paramStr += parameters.get(i) + ", ";
		}
		// exclusive the last ", "
		if(paramStr.length() > 2)
		{
			paramStr = paramStr.substring(0, paramStr.length() - 2);
		}
		
		//print function header
		System.out.println(padding + "function " + funName + "(" + paramStr + ")");
		System.out.println(padding + "{");
		
		/* parse function block */
		Tree block = root.getChild(2);
		traverseBlock(block, symbolTab, padding + INDENT);
		System.out.println(padding + "}\n");
		return funName;
	}
	
	/**
	 * parser simple statement and return variables name if any
	 * @param root
	 * @param symtab
	 * @return
	 */
	protected ArrayList<String> traverseSimpleStmt(Tree root, Hashtable<String, String> symtab, String padding)//, int shift)
	{
		//Hashtable<String, String> symboltab = null;
		assert(root.getChildCount() >= 1);
		root = root.getChild(0);
		if(root.getType() == PythonParser.EXPRESSION_STATMENT)
		{
			boolean isAssignExpr = false;
			int i;
			int childCount = root.getChildCount();
			ArrayList<String> leftVar = new ArrayList<String>();
			String rightPart = "";
			boolean mapAssign = false;
			String leftStr = "";
			
			/** assign expression or function reference? */
			for(i = 0; i < childCount; i++)
			{
				if(root.getChild(i).getType() == PythonParser.ASSIGN)
				{
					isAssignExpr = true;
					break;
				}
			}
			
			/** if is assign expression, extracts left part of the expression */
			if(isAssignExpr)     //parse assign expression
			{
				int j;	
				//check variables
				for(j = 0; j < i; j ++)
				{
					if(root.getChild(j).getType() == PythonParser.VARIABLE)
					{
						String varName = root.getChild(j).getChild(0).getText();
						leftVar.add(varName);
						if(!symtab.containsKey(varName))
						{
							System.out.println(padding + "var " + varName  + ";");   //add declaration
							//symbol reference to paranet's simbol table
							addToSymbolTable(symtab, varName);
						}
						else// if(symtab.get(varName) == "err")
						{
							varName = symtab.get(varName);
						}
						leftStr += varName;
					}
					else
					{
						leftStr +=  root.getChild(j).getText();
						if(root.getChild(j).getType() == PythonParser.COMMA)       //left part contains more than one variables
						{
							mapAssign = true;
						}
					}
				}
				i++;             // ignore "="
			}
			else
			{
				i = 0;
			}
			
			/** parse right part of the expression */
			while(i < childCount)
			{
				switch(root.getChild(i).getType())
				{
					case PythonParser.VARIABLE:
					{
						String varName = root.getChild(i).getChild(0).getText().trim();
						if(!symtab.containsKey(varName))
						{
							System.out.println(padding + "var " + varName  + ";");   //add declaration
							//symbol reference to paranet's simbol table
							addToSymbolTable(symtab, varName);
						}
						else
						{
							varName = symtab.get(varName);
						}
						
						rightPart += varName;
						break;
					}
					case PythonParser.BRACKETED:
					{
						//initialize a array
						int k;
						ArrayList<String> maps = new ArrayList<String>();
						//extract out all variables
						for(k = 0; k < root.getChild(i).getChildCount(); k++)
						{
							if(root.getChild(i).getChild(k).getType() == PythonParser.VARIABLE)
							{
								String varName = root.getChild(i).getChild(k).getChild(0).getText().trim();
								if(!symtab.containsKey(varName))
								{
									System.out.println(padding + "var " + varName  + ";");   //add declaration
									//symbol reference to paranet's simbol table
									addToSymbolTable(symtab, varName);
								}
								else
								{
									varName = symtab.get(varName);
								}
								maps.add(varName);
							}
						}
						//combine them into a array
						String arrayName = "[";
						for(k = 0; k < maps.size(); k++)
						{
							arrayName += maps.get(k) + ",";
						}
						arrayName = arrayName.substring(0, arrayName.length() - 1);
						arrayName += "]";
						/*
						String arrayName = "_new_Arr" + Walker.newCount;
						Walker.newCount++;
						System.out.println(padding + "var " + arrayName + " = [];");
						//assign value
						for(k = 0; k < maps.size(); k++)
						{
							System.out.println(padding + arrayName + "[" + k + "] = " + maps.get(k) + ";");
						}*/
						//change to new tag
						rightPart += arrayName;
						break;
					}
					default:
					{
						String factor = root.getChild(i).getText().trim();
						if(symtab.containsKey(factor))
						{
							factor = symtab.get(factor);
						}
						rightPart += ""+ factor + "";
					}
				}
				i++;
			}
			
			/** combine information */
			if(isAssignExpr)
			{
				if(mapAssign)        //assign to more than one var
				{
					int k;
					for(k = 0; k < leftVar.size(); k++)
					{
						System.out.println(padding + leftVar.get(k) + " = " + rightPart + "[" + k +  "];");
					}
				}
				else                          //only assign to one var
				{
					System.out.println(padding + leftStr + " = " + rightPart + ";");
				}
			}
			else						
			{
				System.out.println(padding + rightPart + ";");
			}
		}
		else if(root.getType() == PythonParser.IMPORT_STATMENT)
		{
			/*
			String importStmt = "import " + root.getChild(0).getText() + ";";
			System.out.println(importStmt);*/
		}
		else if(root.getType() == PythonParser.RETURN_STATMENT)
		{
			String returnStmt = root.getChild(0).getText() + " ";
			String retVal = root.getChild(1).getChild(0).getText();
			if(retVal.contains("True"))
			{
				retVal = "true";
			}
			else if(retVal.contains("False"))
			{
				retVal = "false";
			}
			returnStmt += retVal  + ";";
			System.out.println(padding + returnStmt);
		}
		else
		{
			System.out.println("Error: invalid statement.");
		}
		 
		 
		 /*
		switch(root.getType())
		{
			case IMPORT_TYPE:
			{
				System.out.println("IMPORT STMT;");
				break;
			}
			case RETURN_TYPE:
			{
				System.out.println("RETURN STMT;");
				break;
			}
			default:
			{
				//root = root.getChild(0); //SIMPLE_STMT -> EXPRESSION_EXPR
			}
		}*/
		
		return null;
	}
	
	/**
	 * get parametrs (a,b,c,d,...)
	 * @param root
	 * @return {a, b, c}
	 */
	protected ArrayList<String> traverseParameters(Tree root)
	{
		int childCount = root.getChildCount();
		assert(childCount % 2 == 1);
		ArrayList<String> params = new ArrayList<String>();
		int i;
		for(i = 0; i < childCount; i+= 2)
		{
			params.add(root.getChild(i).getText());
		}
		return params;
	}
	
	/**
	 * 
	 * @param root
	 * @param symtab
	 */
	protected void traverseBlock(Tree root, Hashtable<String, String> symtab, String padding)
	{
		int childCount = root.getChildCount();
		int i;
		@SuppressWarnings("unchecked")
		Hashtable<String, String> symboltab = (Hashtable<String, String>)symtab.clone();
		for(i = 0; i < childCount; i++)
		{
			switch(root.getChild(i).getType())
			{
				case PythonParser.SIMPLE_STATMENT:
				{
					traverseSimpleStmt(root.getChild(i), symboltab, padding);
					//System.out.println("SIMPLE_STATEMENT;");
					break;
				}
				case PythonParser.WHILE_STATMENT:      //pass down symbol table directly
				{
					ArrayList<Tree> whiles = new ArrayList<Tree>();
					Tree whileStmt = root.getChild(i);
					int k;
					for(k = 1; k < whileStmt.getChildCount() ; k++)
					{
						whiles.add(whileStmt.getChild(k));
					}
					/*
					while(root.getChild(i).getType() != PythonParser.BLOCK)
					{
						whiles.add(root.getChild(i));
						i++;
					}*/
					//whiles.add(root.getChild(i));
					traverseWhileStmt( whiles, symboltab, padding);
					break;
				}
				case PythonParser.IF_STATMENT:	   //pass down symbol table directly
				{
					ArrayList<Tree> ifs = new ArrayList<Tree>();
					Tree ifStmt = root.getChild(i);
					int k;
					for(k = 1; k < ifStmt.getChildCount() ; k++)
					{
						ifs.add(ifStmt.getChild(k));
					}
					/*
					i++;
					while(root.getChild(i).getType() != PythonParser.BLOCK)
					{
						ifs.add(root.getChild(i));
						i++;
					}*/
					//ifs.add(root.getChild(i));
					traverseIfStmt( ifs, symboltab, padding);
					break;
				}
				case PythonParser.FOR_STATMENT:
				{
					//var in object
					//maybe has a ELSE_STATEMENT
					
					break;
				}
				case PythonParser.CLASS_DEF:
				{
					
					break;
				}
				case PythonParser.TRY_STATMENT:
				{
					//child[0]: try's block
					//rest: except statement or finally statement
					Tree tryBlock = root.getChild(i);
					System.out.println(padding + "try");
					System.out.println(padding + "{");
					traverseBlock(tryBlock.getChild(0), symboltab, padding + INDENT);
					System.out.println(padding + "}");
					int k;
					for(k = 1; k < tryBlock.getChildCount(); k++)
					{
						switch(tryBlock.getChild(k).getType())
						{
							case PythonParser.EXCEPT_STATMENT:
							{
								Tree exceptBlock = tryBlock.getChild(k);
								assert(exceptBlock.getChildCount() >= 1);
								Hashtable<String, String> excpetSymbtab;
								int t;
								String errStr = "";
								for(t = 0; t < exceptBlock.getChildCount(); t++)
								{
									switch(exceptBlock.getChild(t).getType())
									{
										case PythonParser.VARIABLE:  
										{
											//add var to symbol table
											String var = exceptBlock.getChild(t).getChild(0).getText().trim();
											addToSymbolTable(symboltab, var, "err");
											errStr += var;
											break;
										}
										default:  // sth i don't know
										{
											errStr += exceptBlock.getChild(t).getText();
											break;
										}
									}
									
								}
								//System.out.println(padding + "catch(" + errStr + ")");
								System.out.println(padding + "catch(err)");
								System.out.println(padding + "{");
								assert((k+1) <tryBlock.getChildCount());
								excpetSymbtab = (Hashtable<String, String>)symboltab.clone();
								traverseBlock(tryBlock.getChild(k+1),excpetSymbtab, padding + INDENT);
								k++;
								System.out.println(padding + "}");
								break;
							}
							case PythonParser.FINALLY_STATMENT:
							{
								assert(tryBlock.getChild(k).getChildCount() == 1);
								System.out.println(padding + "finally");
								System.out.println(padding + "{");
								traverseBlock(tryBlock.getChild(k).getChild(0),symboltab, padding + INDENT);
								System.out.println(padding + "}");
								break;
							}
						}
					}
					break;
				}
				default:
				{
					System.out.println("Error in child[" + i + "] under root " + root.getText());
					break;
				}
			}
			//System.out.println("Debug: " + root.getChild(i).getText() + " type: " + root.getChild(i).getType());
		}
	}
	
	/**
	 * return list of variables that need 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected ArrayList<String> traverseWhileStmt(ArrayList<Tree> root,  Hashtable<String, String> symtab, String padding)
	{
		//this time it have "while V reop V block"
		assert(root.size() > 1);
		ArrayList<String> retVal = new ArrayList<String>();
		Hashtable<String, String> symboltab;
		int i;
		String relOpStr = "";
		for(i = 0; i < root.size() - 1; i++)
		{	
			switch(root.get(i).getType())
			{
				case PythonParser.VARIABLE:
				{
					String varName = root.get(i).getChild(0).getText();
					if(!symtab.containsKey(varName))
					{
						System.out.println(padding + "var " + varName  + ";");   //add declaration
						retVal.add(varName);
						//symbol reference to paranet's simbol table
						addToSymbolTable(symtab, varName);
					}
					relOpStr += varName + " ";
					break;
				}
				default:
				{
					relOpStr += root.get(i).getText() + " ";
					break;
				}
			}
		}
		symboltab = (Hashtable<String, String>)symtab.clone();
		//print while (relOpStr) { Block }
		System.out.println(padding + "while(" + relOpStr + ")");
		System.out.println(padding + "{");
		traverseBlock(root.get(root.size() - 1), symboltab, padding + INDENT);
		System.out.println(padding + "}");
		return retVal;
	}
	
	
	/**
	 * return list of variables that need 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected ArrayList<String> traverseForStmt(ArrayList<Tree> root,  Hashtable<String, String> symtab, String padding)
	{
		//this time it have "while V reop V block"
		assert(root.size() > 1);
		ArrayList<String> retVal = new ArrayList<String>();
		Hashtable<String, String> symboltab;
		int i;
		String relOpStr = "";
		for(i = 0; i < root.size() - 1; i++)
		{	
			switch(root.get(i).getType())
			{
				case PythonParser.VARIABLE:
				{
					String varName = root.get(i).getChild(0).getText();
					if(!symtab.containsKey(varName))
					{
						System.out.println(padding + "var " + varName  + ";");   //add declaration
						retVal.add(varName);
						//symbol reference to paranet's simbol table
						addToSymbolTable(symtab, varName);
					}
					relOpStr += varName + " ";
					break;
				}
				default:
				{
					relOpStr += root.get(i).getText() + " ";
					break;
				}
			}
		}
		symboltab = (Hashtable<String, String>)symtab.clone();
		//print while (relOpStr) { Block }
		System.out.println(padding + "for(" + relOpStr + ")");
		System.out.println(padding + "{");
		traverseBlock(root.get(root.size() - 1), symboltab, padding + INDENT);
		System.out.println(padding + "}");
		return retVal;
	}
	
	/*
	protected void traverseExceptStmt(Tree root, Hashtable<String, String> errList, Hashtable<String, String> symTab, String padding)
	{
		
	}*/
	
	/**
	 * return list of variables that need 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected ArrayList<String> traverseIfStmt(ArrayList<Tree> root,  Hashtable<String, String> symtab, String padding)
	{
		assert(root.size() > 1);
		ArrayList<String> retVal = new ArrayList<String>();
		Hashtable<String, String> symboltab;
		int i;
		String relOpStr = "";
		for(i = 0; i < root.size() - 1; i++)
		{	
			switch(root.get(i).getType())
			{
				case PythonParser.VARIABLE:
				{
					String varName = root.get(i).getChild(0).getText();
					if(!symtab.containsKey(varName))
					{
						System.out.println(padding + "var " + varName  + ";");   //add declaration
						retVal.add(varName);
						//symbol reference to paranet's simbol table
						addToSymbolTable(symtab, varName);
					}
					relOpStr += varName + " ";
					break;
				}
				default:
				{
					relOpStr += root.get(i).getText() + " ";
					break;
				}
			}
		}
		symboltab = (Hashtable<String, String>)symtab.clone();
		//print if (relOpStr) { Block }
		System.out.println(padding + "if(" + relOpStr + ")");
		System.out.println(padding + "{");
		traverseBlock(root.get(root.size() - 1), symboltab, padding + INDENT);
		System.out.println(padding + "}");
		return retVal;
	}
	
	
	protected ArrayList<String> traverseClassDef(Tree root,Hashtable<String, String> symtab, String padding){
		ArrayList<String> retVal = new ArrayList<String>();
		Hashtable<String, String> symboltab = (Hashtable<String, String>) symtab.clone();
		
		
		
		return retVal;
	}
	/**
	 * 
	 * @param symboltab
	 * @param symbol
	 */
	protected void addToSymbolTable(Hashtable<String, String> symboltab , String symbol)
	{
		if(symboltab.containsKey(symbol))
		{
			System.out.println("Deplicate symbol: " + symbol);
		}
		else
		{
			symboltab.put(symbol, symbol);
		}
	}
	
	/**
	 * 
	 * @param symboltab
	 * @param symbol
	 */
	protected void addToSymbolTable(Hashtable<String, String> symboltab , String symbol, String value)
	{
		if(symboltab.containsKey(symbol))
		{
			System.out.println("Deplicate symbol: " + symbol);
		}
		else
		{
			symboltab.put(symbol, value);
		}
	}
	
	
    public static class MyLexer extends PythonLexer {
	public MyLexer(CharStream lexer) {
	    super(lexer);
	}
	public Token nextToken() {
	    startPos = getCharPositionInLine();
	    return super.nextToken();
	}
    }
	
    public static void main(String[] args) throws Exception {
		CharStream input = new ANTLRFileStream(args[0]);
		PythonLexer lexer = new MyLexer(input);
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		//tokens.discardOffChannelTokens(true);
		PythonTokenSource indentedSource = new PythonTokenSource(tokens);
		tokens = new CommonTokenStream(indentedSource);
		//System.out.println("tokens="+tokens.getTokens());
		PythonParser parser = new PythonParser(tokens);
		//parser.file_input();
		PythonParser.file_input_return returnResult = parser.file_input();
		CommonTree treeRoot = (CommonTree) returnResult.getTree();
		//for (int i = 0; i < treeRoot.getChildCount(); i++) {
		//	System.out.println(treeRoot.getChild(i).getType());
		//}
		//System.out.println(treeRoot.toStringTree());
		//System.out.print(Main._getLongText(treeRoot));
		//PythonASTReader reader = new PythonASTReader(treeRoot);
		//reader.printFunctions();
		Walker walker = new Walker(treeRoot);
		walker.traverseRoot();
    }	
}
